#include <iostream>
#include <pthread.h>
#include <unistd.h> // sleep
using namespace std;
/*
   除了c++11及以后的版本外，c++11下及c语言本身也不支持多线程
   但linux系统库提供了pthread库来支持多线程

   头文件：#include<pthread.h>
   编译时：需要加上 -pthread 选项，链接上对于的线程支持库


创建线程：
    #include <pthread.h>

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
    功能：创建一个分支线程
    参数1：通过参数返回子线程号，用法：在外部定义一个该类型的变量，将地址传递入函数，调用结束后，该变量中即是线程号
    参数2：线程属性，一般填NULL，让系统使用默认属性创建一个线程
    参数3：是一个回调函数，一个函数指针，需要向该参数中传递一个函数名，作为线程体执行函数该函数由用户自己定义，参数是void*类型，返回值也是void *类型
    参数4： 是参数3的参数，如果不想向线程体内传递数据，填NULL即可
    返回值：成功返回0，失败返回一个错误码（非linux内核的错误码，是线程支持库中定义的一个错误码）

    Compile and link with -pthread.
    //编译时需要加上 -pthread选项

注意：
  1.进程是资源分配的基本单位，而线程是任务器进行任务调度的最小单位
  2.一个进程一定有一个主线程，主线程的线程号是进程号
  3.主进程结束，进程就结束了即其他分支线程自动结束
 */
// 无参创建线程(线程体函数必须是 void* 类型)
void *task1(void *arg)
{
    while (1)
    {
        printf("分支线程在运行\n");
        sleep(1);
    }
    return NULL; // 线程体函数执行结束后，返回一个NULL指针,不写会警告
}

int test1()
{
    pthread_t tid = -1; // unsigned int
    if (pthread_create(&tid, NULL, task1, NULL) != 0)
    // 参数2：表示让系统使用默认属性创建一个线程
    // 参数3：线程体函数名
    // 参数4：表示向线程体中传递的数据
    {
        printf("tid create error\n");
        return -1;
    }

    printf("分支线程的tid = %#lx\n", tid); // #lx 表示长整型下的16进制输出
    while (1)
    { // 防止主线程提前结束
        printf("主线程在运行！！\n");
        sleep(1);
    }
    return 0;
}
//------------------------------------------------------------------------------------
// 一个参数创建线程
void *task2(void *arg)
{
    // arg --> &num  但是arg是一个void*类型的变量，需要转换为具体指针进行操作
    //(int*)arg    --->将其转换为整型的指针
    //*(int *)arg  --->num的值
    int key = *(int *)arg;
    printf("我是分支线程：num = %d\n", key); // 1314
    /*
    为什么不是520呢？
    因为我们的linux是单核cpu,是时间片轮换机制并发实现的
    主线性已经运行到该值为1314时候，分支线程还会运行呢
    */
    return NULL;
}

int test2()
{
    pthread_t tid = -1;
    int num = 520;
    if (pthread_create(&tid, NULL, task2, &num) != 0)
    // 参数4：传递地址的
    {
        printf("tid create error\n");
        return -1;
    }

    num = 1314;
    printf("主线程的num = %d\n", num);
    while (1)
        ;
    return 0;
}
//------------------------------------------------------------------------------
// 多个参数创建线程
// 所有线程共享：全局变量、堆内存(动态分配)、代码段,栈区是不共享的，所以必须全局区声明结构体
// 使用结构体来储存多参数
struct Info
{
    int num;
    char name[20];
    double score;
};

void *task3(void *arg)
{
    Info buf = *((Info *)arg);

    // 将结构体指针转换为结构体变量
    printf("分支线程中：num = %d, name = %s, score = %.2lf\n", buf.num, buf.name, buf.score);
    return NULL;
}

int test3()
{

    int num = 520;
    char name[20] = "zhangsan";
    double score = 99.5;
    // 需求：将上面的三个数据全部传入线程体中
    Info buf = {num, "zhangsan", score};

    pthread_t tid = -1;
    if (pthread_create(&tid, NULL, task3, &buf) != 0)
    {
        printf("tid create error\n");
        return -1;
    }

    printf("主线程while阻塞等待其他线程结束\n");
    while (1)
        ;
}

int main(int argc, const char *argv[])
{
    // test1(); // 无参创建线程
    // test2(); // 一个参数创建线程
    test3(); // 多个参数创建线程
    return 0;
}